#pragma once
#include <condition_variable>
#include <mutex>

#include "util.h"

class semaphore {
public:
    explicit semaphore(size_t initial = 0) {
        DG_UNUSED(initial);
        _count = 0;
    }

    ~semaphore() {}

    void post(size_t n = 1) {
        std::unique_lock<std::recursive_mutex> lock(_mutex);
        _count += n;
        if (n == 1) {
            _condition.notify_one();
        } else {
            _condition.notify_all();
        }
    }

    void wait() {
        std::unique_lock<std::recursive_mutex> lock(_mutex);
        while (_count == 0) {
            _condition.wait(lock);
        }
        --_count;
    }

    bool wait_for(uint32_t ms) {
        std::cv_status status = std::cv_status::no_timeout;

        std::unique_lock<std::recursive_mutex> lock(_mutex);
        while (_count == 0 && status != std::cv_status::timeout) {
            status = _condition.wait_for(lock, std::chrono::milliseconds(ms));
        }
        if (status != std::cv_status::timeout) {
            --_count;
            return true;
        }

        return false;
    }

private:
    size_t _count;
    std::recursive_mutex _mutex;
    std::condition_variable_any _condition;
};